package com.ssl.ware.service;

import com.ssl.ware.pojo.*;
import com.ssl.ware.repo.CityInfoRepo;
import com.ssl.ware.repo.FreightRepo;
import com.ssl.ware.repo.ProductFreightRepo;
import com.ssl.ware.repo.WarehouseRepo;
import com.ssl.ware.utils.DistanceCalculateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class WarehouseService {
    @Autowired
    private FreightRepo freightRepo;

    @Autowired
    private ProductFreightRepo productFreightRepo;

    @Autowired
    private CityInfoRepo cityInfoRepo;

    @Autowired
    private WarehouseRepo warehouseRepo;

    public Integer calculateFreight(List<OderPageRequest> requestList) {
        Integer totalFreight = 0;
        //因为我们需要按店铺进行费用的计算，所以我们需要对 requestList 进行按 shopId 的聚合
        Map<Integer, List<OderPageRequest>> shopMap =
                requestList.stream().collect(Collectors.groupingBy(OderPageRequest::getShopId));
        //按每个shopId进行运费计算
        Set<Integer> shopIds = shopMap.keySet();
        for (Integer shopId : shopIds) {
            List<OderPageRequest> oderPageRequests = shopMap.get(shopId);
            //根据shopId和skuIds获取全部的 product-freight 表数据，并且得出数据后，根据skuids进行一个聚合
            List<ProductFreight> productFreights = productFreightRepo.getByShopIdAndSkuIdIn(shopId,
                    oderPageRequests.stream().map(OderPageRequest::getSkuId).collect(Collectors.toList()));
            Map<Long, List<ProductFreight>> productFreightMap = productFreights.stream()
                    .collect(Collectors.groupingBy(ProductFreight::getSkuId));

            //根据shopId和模版名字获取所有的freight数据
            List<Freight> freights = freightRepo.getByShopIdAndTemplateNameIn(shopId,
                    productFreights.stream().map(ProductFreight::getFreightTemplateName).collect(Collectors.toList()));
            Map<String, List<Freight>> freightMap = freights.stream()
                    .collect(Collectors.groupingBy(Freight::getTemplateName));

            //根据shopId拿到仓库名称
            List<Warehouse> warehouses = warehouseRepo.getByShopId(shopId);
            int basePrice = 0;
            int continuationPrice = 0;
            int perUnitPrice = 0;
            boolean hasCalculateDistance = false;
            int distanceContinuationPrice = 0;
            //确定一下当前所有的order商品用的模版数量
            for (OderPageRequest oderPageRequest : oderPageRequests) {
                List<Freight> currentFreights = freightMap.get(productFreightMap.get(oderPageRequest.getSkuId()).get(0).getFreightTemplateName());
                //计算基本运费
                if (freightMap.size() > 1) { //如果有多种类的模版，比如说有重量，距离，按最大的 base_price计费
                    for (Freight cf : currentFreights) {
                        basePrice = basePrice >= cf.getBasePrice() ? basePrice : cf.getBasePrice();
                    }
                } else {//如果是 一种模版，比如说只有重量，基础运费使用 单价最高的 （base_price / base_count） 作为我们的基础运费=base_price
                    for (Freight cf : currentFreights) {
                        if (perUnitPrice < cf.getBasePrice() / cf.getBasCount()) {
                            basePrice = cf.getBasePrice();
                            perUnitPrice = cf.getBasePrice() / cf.getBasCount();
                        }
                    }
                }
                //计算续费部分
                for (Freight cf : currentFreights) {
                    switch (cf.getBillingType()) {
                        case 0: //重计
                            String region = cf.getRegion();
                            if (region != null && region.contains(oderPageRequest.getCityCode())){
                                Integer productBaseSize =
                                        productFreightMap.get(oderPageRequest.getSkuId()).get(0).getProductBaseSize();
                                if(productBaseSize > cf.getBasCount()) {
                                    int continuationCount = (productBaseSize - cf.getBasCount()) / cf.getPerContinuationCount() + 1;
                                    continuationPrice += continuationCount * cf.getPerContinuationPrice() * oderPageRequest.getCount();
                                    break;
                                }
                            }
                            break;
                        case 1: //距计
                            if (hasCalculateDistance) {
                                continuationPrice += distanceContinuationPrice;
                                break;
                            }
                            Freight freight = currentFreights.get(0);
                            String customerCityCode = oderPageRequest.getCityCode();
                            Set<String> cityCodes = warehouses.stream().map(Warehouse::getCity).collect(Collectors.toSet());
                            cityCodes.add(customerCityCode);
                            //
                            List<CityInfo> cityInfos = cityInfoRepo.getByCityCodeIn(cityCodes);
                            Map<String, List<CityInfo>> cityInfoMap = cityInfos.stream()
                                    .collect(Collectors.groupingBy(CityInfo::getCityCode));
                            Integer minDistanceKMeters = null;
                            cityCodes.remove(customerCityCode);
                            for(String cityCode : warehouses.stream().map(Warehouse::getCity).collect(Collectors.toSet())) {
                                Integer distance = DistanceCalculateUtils.calculateDistance(cityInfoMap.get(cityCode).get(0).getCityLati(),
                                        cityInfoMap.get(cityCode).get(0).getCityLongti(),
                                        cityInfoMap.get(customerCityCode).get(0).getCityLati(),
                                        cityInfoMap.get(customerCityCode).get(0).getCityLongti());
                                minDistanceKMeters = minDistanceKMeters == null ? distance :
                                        minDistanceKMeters <= distance ? minDistanceKMeters : distance;
                            }
                            distanceContinuationPrice = minDistanceKMeters <= freight.getBasCount() ? 0 :
                                    ((minDistanceKMeters - freight.getBasCount()) / freight.getPerContinuationCount() + 1)
                                            * freight.getPerContinuationPrice();
                            // 如果按距离计算，我们是不是还需要乘以 物品数量啊？ 这样对客户不公平
                            // 不同的物品种类，需要不要计算呢？ 需要
                            // 按距离计算的话，按物品种类进行追加
                            continuationPrice += distanceContinuationPrice;
                            hasCalculateDistance = true;
                            break;
                        default:
                            break;
                    }
                }
            }
            totalFreight = totalFreight + basePrice + continuationPrice;
        }
        return totalFreight;
    }
}
